#include <stdafx.h>
#include "Utilities/utilities.h"
#include "Shader.h"
#include "Vertex.h"
#include "Math.h"
#include "Controller/Scene.h"
#include "Controller/Camera.h"
#include "Globals.h"


Shader::Shader() 
	: m_states(NULL), m_isBlend(false)
{
}

Shader::~Shader() 
{
}

void Shader::SetVSFileName(const char * strVS)
{
	strcpy_s(m_fileVS, 255, strVS);
}

void Shader::SetFSFileName(const char * strFS)
{
	strcpy_s(m_fileFS, 255, strFS);
}

int Shader::Init()
{
	m_vertexShader = esLoadShader(GL_VERTEX_SHADER, m_fileVS);
	if ( m_vertexShader == 0 )
		return -1;

	m_fragmentShader = esLoadShader(GL_FRAGMENT_SHADER, m_fileFS);
	if ( m_fragmentShader == 0 )
	{
		glDeleteShader( m_vertexShader );
		return -2;
	}

	m_program = esLoadProgram(m_vertexShader, m_fragmentShader);

	//finding location of uniforms / attributes
	m_positionAttribute = glGetAttribLocation(m_program, "a_pos_local");
	m_normAttribute = glGetAttribLocation(m_program, "a_norm_local");
	m_uvAttribute = glGetAttribLocation(m_program, "a_uv");

	m_wvpUniform = glGetUniformLocation(m_program, "u_WVP");
	m_wUniform = glGetUniformLocation(m_program, "u_W");

	m_textureUniform = glGetUniformLocation(m_program, "u_texture");
	m_multiTexturesUniform[0] = glGetUniformLocation(m_program, "u_texture_0");
	m_multiTexturesUniform[1] = glGetUniformLocation(m_program, "u_texture_1");
	m_multiTexturesUniform[2] = glGetUniformLocation(m_program, "u_texture_2");
	m_multiTexturesUniform[3] = glGetUniformLocation(m_program, "u_texture_3");

	m_cameraPosUniform = glGetUniformLocation(m_program, "u_camera_pos");
	m_cameraWMatrixUniform = glGetUniformLocation(m_program, "u_camera_W");

	//for blend option
	if (m_isBlend)
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	return 0;
}

void Shader::PrepareVariables(Matrix &W)
{
	Camera * camera = SCENE->GetCamera();

	glUseProgram(m_program);

	// WVP and W
	if (m_wvpUniform != -1)
	{
		Matrix MVP = W * camera->VPMatrix();
		glUniformMatrix4fv(m_wvpUniform, 1, GL_FALSE, (GLfloat *) &MVP);
	}
	if (m_wUniform != -1)
	{
		glUniformMatrix4fv(m_wUniform, 1, GL_FALSE, (GLfloat *) &W);
	}

	// Camera uniform
	if (m_cameraWMatrixUniform != -1)
	{
		glUniformMatrix4fv(m_cameraWMatrixUniform, 1, GL_FALSE, (GLfloat *) &camera->m_W);
	}
	if (m_cameraPosUniform != -1)
	{
		glUniform3f(m_cameraPosUniform, camera->m_position.x, camera->m_position.y, camera->m_position.z);
	}

	ToggleStates(true);
}

void Shader::ToggleStates(bool bEnable)
{
	for (unsigned int i = 0; i < m_states.size(); i++)
	{
		if (bEnable)
			glEnable(m_states.at(i));
		else
			glDisable(m_states.at(i));
	}
}

void Shader::CleanUp()
{
	glDeleteProgram(m_program);
	glDeleteShader(m_vertexShader);
	glDeleteShader(m_fragmentShader);
}